home *** CD-ROM | disk | FTP | other *** search
/ PC-X 1997 October / pcx14_9710.iso / swag / files.swg / 0107_Handy TP7 Long file name unit.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-30  |  39.8 KB  |  1,234 lines

  1. {
  2. Hi,
  3.   here's another contribution - an LFN unit which is actually useful. It
  4. allows working with near-normal TP/TPW commands, transparently on LFN
  5. and non-LFN disks. Enjoy.
  6.  
  7. Eyal Doron
  8. }
  9.  
  10. {$IFDEF WINDOWS}
  11. {$N-,V-,W-,G+}
  12. {$ELSE}
  13. {N-,V-,G+}
  14. {$ENDIF}
  15.  
  16. Unit lfnunit;
  17.  
  18. {========================================================================}
  19. { LFNUnit - A long filename support unit for TP6 and TPW1.5.             }
  20. { Written by Eyal Doron, doron@physics,technion.ac.il, June 1997.        }
  21. { Released into the public domain.                                       }
  22. {                                                                        }
  23. { This is a unit to support long filenames in Win95 and WinNT, for use   }
  24. { in ordinary 16-bit programs in Turbo Pascal 6.0 and Turbo Pascal for   }
  25. { Windows 1.5. It should be a simple matter to adapt to TP/BP 7 as well. }
  26. { The unit is built to support LFN if available, and the usual FAT16     }
  27. { format if not, in a transparent manner, i.e. the programmer should not }
  28. { worry whether LFN is supported or not, the routines work the same in   }
  29. { both cases. The unit is not complete, in the sense that not all of the }
  30. { interrupts are supported, but the main thrust is the enhancement of    }
  31. { the Turbo Pascal I/O scheme to support LFN in as natural a way as      }
  32. { possible.                                                              }
  33. {                                                                        }
  34. { The unit contains three families of procedures and functions:          }
  35. { 1) Basic LFN API: This is a set of procedures and functions that give  }
  36. {    access to the LFN interrupts: FindFirst/Next/Close, short and long  }
  37. {    names, time, attributes and creation.                               }
  38. { 2) Service routines. These routines make use of the LFN API to mimic   }
  39. {    the operation of the DOS or WinDos supplied routines, only with LFN }
  40. {    support. I chose to mimic the TP6.0 routines, rather than the TPW   }
  41. {    ones, because I prefer Pascal-type strings to C-type strings, but   }
  42. {    its a simple matter to add these as well. All the routines return   }
  43. {    their error codes inside the DOS/WinDos global "DosError".          }
  44. { 3) Input-output support. This set of procedures and functions defines  }
  45. {    the paradigm for LFN support in Turbo Pascal. It "rides" on top of  }
  46. {    the usual file variables ("file", "file of" and "text"), and stores }
  47. {    the additional LFN info in the UserData field of the file records.  }
  48. {    The routines have an interface which is similar to the TP ones,     }
  49. {    namely "LFNAssign" is equivalent to "Assign", "LFNRewrite" to       }
  50. {    "Rewrite", and so on. This paradigm enables use of the usual Pascal }
  51. {    I/O scheme and routines with long file names, in an almost          }
  52. {    transparent manner. The differences are:                            }
  53. {    a) Before using a file variable it has to be initialized by calling }
  54. {       LFNNew. After you are done with it, you should call LFNDispose   }
  55. {       to free the allocated memory.                                    }
  56. {    b) You MUST consistently use LFNNew, LFNAssign, LFNRewrite,         }
  57. {       LFNRename and LFNDispose in order to support LFN. The other      }
  58. {       routines are optional, providing error detection and consistent  }
  59. {       error trapping, but the TP equivalents should also work.         }
  60. {    c) LFNReset, LFNRewrite and LFNAppend always accept a RecLen        }
  61. {       parameter, which is optional in Reset and Rewrite and missing in }
  62. {       Append. This is because TP does not support overloading. The     }
  63. {       parameter is ignored for text files and when it is zero.         }
  64. {    d) LFNAppend differs from Append also in that if the file does not  }
  65. {       exist, Append reports a DOS error, while LFNAppend creates it    }
  66. {       using LFNRewrite.                                                }
  67. {    e) LFNFindFirst/LFNFindNext return the name as an AsciiZ string,    }
  68. {       not a Pascal string, even in TP6, for the sake of consistency.   }
  69. {    f) All the routines return the error code in the DosError global    }
  70. {       Dos/WinDos variable, and most of them also return it as a        }
  71. {       functional result. Additionally, the "LFNRuntimeErrors" global   }
  72. {       boolean variable controls the generation of runtime errors.      }
  73. {                                                                        }
  74. { Comments, bug reports, etc. are welcome.                               }
  75. {========================================================================}
  76.  
  77.  
  78. Interface
  79.  
  80. Uses
  81. {$IFDEF WINDOWS}
  82.   WinDos,WObjects,WinTypes,WinProcs,strings;
  83. {$ELSE}
  84.   Dos,Objects;
  85. {$ENDIF}
  86.  
  87. const
  88.   ShortPathName = 79;
  89.   LFNRuntimeErrors: boolean = false; { Determines if runtime errors are generated }
  90.  
  91.   LFNErr_Uninitialized = 120; { LFN routines called before LFNAssign }
  92.   LFNErr_NotAllocated  = 121; { LFN routines called before LFNNew    }
  93.   LFNErr_NotATextFile  = 122; { Appending to a non-text file         }
  94.  
  95. {$IFDEF WINDOWS}
  96.   ofn_LongNames = $00200000;  { Required to support LFN in the common dialogs. }
  97.                               { OR it into the Flags record of TOpenFilename.  }
  98. {$ENDIF}
  99.  
  100. type
  101.   ShortPathStr = string[ShortPathName];
  102. {$IFNDEF WINDOWS}
  103.   TSearchRec = SearchRec;
  104.   TDateTime = DateTime;
  105.   PChar = ^Char;
  106. {$ENDIF}
  107.  
  108.   TLFNSearchRec = record
  109.     Attr         : longint;                      
  110.     Creation     : comp;                     
  111.     LastAccess   : comp;                   
  112.     LastMod      : comp;             
  113.     HighFileSize : longint; { high 32 bits }             
  114.     Size         : longint; { low 32 bits  }              
  115.     Reserved     : comp;                     
  116.     Name         : array[0..259] of char;        
  117.     ShortName    : array[0..13] of char;    
  118.     Handle       : word;                       
  119.   end;
  120.   PLFNSearchRec = ^TLFNSearchRec;
  121.   { Form used for old-style searches, with an embedded TSearchRec }
  122.   TLFNShortSearchRec = record
  123.     Attr         : longint;
  124.     Creation     : comp;                     
  125.     LastAccess   : comp;                   
  126.     LastMod      : comp;             
  127.     HighFileSize : longint;              
  128.     Size         : longint;               
  129.     Reserved     : comp;                     
  130.     Name         : array[0..13] of char;
  131.     SRec         : TSearchRec;
  132.     Filler       : array[1..260-14-sizeof(TSearchRec)] of byte;       
  133.     ShortName    : array[0..13] of char;    
  134.     Handle       : word;                       
  135.   end;
  136.   PLFNShortSearchRec = ^TLFNShortSearchRec;
  137.  
  138.   { A record to isolate the UserData parameters } 
  139.   TLFNFileParam = record
  140.     Handle     : word;                   { The file handle                  }
  141.     Mode       : word;                   { The file mode                    } 
  142.     Res1       : array[1..28] of byte;   { Everything else up to UserData   }
  143.     { Begin UserData }
  144.     lfname     : PString;                { The long filename in String form }
  145.     plfname    : PChar;                  { The long filename in AsciiZ form }
  146.     TextFile   : boolean;                { Is it a text or binary file      }
  147.     Initialized: boolean;                { Has it been LFNAssigned          }
  148.     Magic      : string[3];              { ID to check LFNNew               }
  149.     Res2       : array[0..1] of byte;    { 2 bytes left in UserData         }
  150.     { End UserData }
  151.     SName      : array[0..79] of char;   { The short filename               }
  152.   end;
  153.   PLFNFileParam = ^TLFNFileParam;
  154.  
  155. var
  156.   LFNAble: boolean;   { Is LFN supported or not. Upon startup it is determined }
  157.                       { by the OS, but can be switched off later if need be.   }
  158.  
  159. function LFNToggleSupport(on: boolean): boolean;
  160.  
  161. {$IFNDEF WINDOWS}
  162. { I need these to access the Srec.Name field properly }
  163. function PCharOf(var F): Pchar;
  164. function StrPas(P: PChar): string;
  165. {$ENDIF}
  166.  
  167. function PChar2Pstring(F: Pchar): PString;
  168. function PString2PChar(F: Pstring): PChar;
  169.  
  170. { Basic API calls }
  171. function  LFNTimeToDos(var LTime: comp): longint;
  172. function  DosTimeToLFN(var Time: longint; var LTime: comp): word;
  173. function  LGetAttr(Filename: PChar; var Attr: word): word;
  174. function  LRenameFile(FromName,ToName: PChar): word; 
  175. function  LCreateEmpty(fname: PChar): word;
  176. function  LFNFindFirst(filespec: string; attr: word; var S: TLFNSearchRec): word;
  177. function  LFNFindNext(var S: TLFNSearchRec): word;
  178. function  LFNFindClose(var S: TLFNSearchRec): word;
  179. function  LFNShortName(LongName: string): ShortPathStr;
  180. function  LFNLongName(ShortName: ShortPathStr): string;
  181.  
  182. { Service routines }
  183. procedure LFNUnpackTime(var LTime: comp; var DT: TDateTime);
  184. function  LFNGetFAttr(var F; var Attr: word): integer;
  185. function  LFNFileExist(fname: string): boolean;
  186. function  LFNFSearch(Path,DirList: string): string;
  187. procedure LFNFSplit(Path: string; Dir,Name,Ext: PString);
  188. function  LFNFExpand(Path: string): string;
  189. procedure CanonicalFname(var S: string);
  190. function  CanonicalFilename(Fname: PChar): Pchar;
  191.  
  192. { Interface to the Pascal Input/Output routines }
  193. procedure LFNNew    (var F; IsText: boolean);
  194. function  LFNAssign (var F; name: string): integer;
  195. function  LFNRewrite(var F; RecLen: word): integer;
  196. function  LFNAppend (var F; RecLen: word): integer;
  197. function  LFNReset  (var F; RecLen: word): integer;
  198. function  LFNErase  (var F): integer;
  199. function  LFNClose  (var F): integer;
  200. procedure LFNDispose(var F);
  201. function  LFNRename (var F; NewName: string): integer;
  202.  
  203.  
  204. implementation
  205.  
  206. const
  207. {$IFNDEF WINDOWS}
  208.   faReadOnly      =  ReadOnly;
  209.   faHidden        =  Hidden;
  210.   faSysFile       =  SysFile;                
  211.   faVolumeID      =  VolumeID;
  212.   faDirectory     =  Directory;                
  213.   faArchive       =  Archive;                
  214.   faAnyFile       =  AnyFile;
  215. {$ENDIF}
  216.  
  217.   LFNMagic = 'LFN';
  218.  
  219. type
  220.   PSearchRec = ^TSearchRec;
  221.   TByteArray = array[0..$FFF0-1] of char;
  222.   PByteArray = ^TByteArray;
  223.  
  224. {$IFNDEF WINDOWS}
  225. function PCharOf(var F): Pchar;
  226. { A very simple function which returns a pointer to its argument. }
  227. { Its main use is in turning array[...] of char in to PChar, to   }
  228. { simulate the TPW/TP7/BP7 extended syntax.                       }
  229. begin
  230.   PCharOf:=@F;
  231. end;
  232.  
  233. function StrPas(P: PChar): string;
  234. var
  235.   i: integer;
  236.   tmp: PString;
  237. begin
  238.   New(tmp); tmp^:=''; if P=Nil then Exit;
  239.   i:=0;
  240.   while (length(tmp^)<256) and (PByteArray(P)^[i]<>#0) do
  241.   begin
  242.     tmp^:=tmp^+PByteArray(P)^[i]; inc(i);
  243.   end;
  244.   StrPas:=tmp^; Dispose(tmp);
  245. end;
  246.  
  247. function StrLen(P: PChar): integer;
  248. var
  249.   i: integer;
  250. begin
  251.   i:=0;
  252.   if P<>Nil then while (i<$7FFF) and (PByteArray(P)^[i]<>#0) do inc(i);
  253.   StrLen:=i;
  254. end;
  255. {$ENDIF}
  256.  
  257. function PChar2Pstring(F: Pchar): PString;
  258. { This routine changes a PChar (AsciiZ) string to a }
  259. { Pascal-type string, in the same memory location.  }
  260. var
  261.   i,len: integer;
  262. begin
  263.   len:=StrLen(F); if len>255 then len:=255;
  264.   for i:=len downto 1 do PByteArray(F)^[i]:=PByteArray(F)^[i-1];
  265.   F^:=Chr(len);
  266.   PChar2PString:=PString(F);
  267. end;                   { PChar2Pstring }
  268.  
  269. function PString2PChar(F: Pstring): PChar;
  270. { This routine changes a Pascal-type string to an }
  271. { AsciiZ string, in the same memory location.     }
  272. var
  273.   i,len: integer;
  274. begin
  275.   len:=length(F^);
  276.   for i:=1 to len do F^[i-1]:=F^[i]; F^[len]:=#0;
  277.   PString2PChar:=PChar(F);
  278. end;                 { PString2PChar }
  279.  
  280. {$IFDEF WINDOWS}
  281. function SupportsLFN: boolean;
  282. var
  283.   WinVersion: word;
  284. begin
  285. {  SupportsLFN:=false; Exit;}
  286.   WinVersion := LoWord(GetVersion);
  287.   SupportsLFN:=true;
  288.   If ((Lo(WinVersion) =  3)  and                    {windows 95 first}
  289.       (Hi(WinVersion) < 95)) or                     {version is 3.95 }
  290.       (Lo(WinVersion) <  3)  then SupportsLFN := False;
  291. end;
  292. {$ELSE}
  293. function SupportsLFN: boolean; assembler;
  294. asm
  295.   mov ax, $160a
  296.   int $2f
  297.   cmp ax, 0 
  298.   jne @no         { Not running under Windows   }
  299.   cmp bh, 2
  300.   jle @no         { Major version <3            }
  301.   cmp bh, 4
  302.   jge @yes        { Major version >3            }
  303.   cmp bl, 94
  304.   jle @no         { Major version =3, minor <95 }
  305. @yes:
  306.   mov al, true
  307.   jmp @exit
  308. @no:
  309.   mov al, false
  310. @exit:
  311. end;                 { SupportsLFN }
  312. {$ENDIF}
  313.  
  314. function LFNToggleSupport(on: boolean): boolean;
  315. { This routine toggles LFN support on and off, provided }
  316. { the OS supports it. It returns the previous status.   }
  317. begin
  318.   LFNToggleSupport:=LFNAble;
  319.   LFNAble:=on and SupportsLFN;
  320. end;
  321.  
  322. {==============================================================}
  323. { BASIC LFN API CALLS.                                         }
  324. { This is a set of routines which implement the WIn95 LFN API, }
  325. { in Turbo Pascal form.                                        }
  326. {==============================================================}
  327.  
  328. function LFNTimeToDos(var LTime: comp): longint; assembler;
  329. { Convert 64-bit number of 100ns since 01-01-1601 UTC to local DOS format time}
  330. { (LTime is var to avoid putting it on the stack) }
  331. asm
  332.   push ds
  333.   lds si,LTime
  334.   xor bl,bl
  335.   mov ax,71a7h
  336.   int 21h
  337.   pop ds
  338.   mov ax,cx
  339.   cmc
  340.   sbb cx,cx
  341.   and ax,cx
  342.   and dx,cx
  343. end;                { LFNTimeToDos }
  344.  
  345. function DosTimeToLFN(var Time: longint; var LTime: comp): word;
  346. { Convert DOS time to the 64-bit Win95 format }
  347. var
  348.   DosTime,DosDate: word;
  349.   DT: TDateTime;
  350. begin
  351.   UnpackTime(Time,DT); FillChar(LTime,sizeof(LTime),0);
  352.   with DT do
  353.   begin
  354.     DosTime:=(sec div 2) or (min shl 5) or (hour shl 11);
  355.     DosDate:=day or (Month shl 5) or ((Year-1980) shl 9);
  356.   end;
  357.   asm
  358.     mov ax, $71A7
  359.     mov bl, 1
  360.     mov cx, DosTime
  361.     mov dx, DosDate
  362.     mov bh, 0
  363.     les di, LTime
  364.     int $21
  365.     jnc @1
  366.     mov [DosError],ax
  367. @1:
  368.   end;
  369.   DosTimeToLFN:=DosError;
  370. end;                 { DosTimeToLFN }
  371.  
  372. function LGetAttr(Filename: PChar; var Attr: word): word; assembler;
  373. { Get the attributes of a file, PChar syntax }
  374. asm
  375.   push ds
  376.   lds dx,Filename
  377.   mov ax,7143h
  378.   xor bl,bl
  379.   int 21h
  380.   pop ds
  381.   les di,Attr
  382.   mov es:[di],cx
  383.   sbb bx,bx
  384.   and ax,bx
  385.   mov [DosError],ax
  386. end;                      { LGetAttr }
  387.  
  388. function LFindFirst(FileSpec: pchar; Attr: word; var SRec: TLFNSearchRec): word;
  389. assembler;
  390. { Search for files }
  391. asm
  392.   push ds
  393.   lds dx,FileSpec
  394.   les di,SRec
  395.   mov cx,Attr
  396.   xor si,si
  397.   mov ax,714eh
  398.   int 21h
  399.   pop ds
  400.   sbb bx,bx
  401.   mov es:[di].TLFNSearchRec.Handle,ax
  402.   and ax,bx
  403.   mov [DosError],ax
  404. end;
  405.  
  406. function LFindNext(var SRec: TLFNSearchRec): word; assembler;
  407. { Find next file }
  408. asm
  409.   mov ax,714fh
  410.   xor si,si
  411.   les di,SRec
  412.   mov bx,es:[di].TLFNSearchRec.Handle
  413.   int 21h
  414.   sbb bx,bx
  415.   and ax,bx
  416.   mov [DosError],ax
  417. end;
  418.  
  419. function LFindClose(var SRec: TLFNSearchRec): word; assembler;
  420. { Free search handle }
  421. asm
  422.   mov ax,714fh
  423.   mov bx,es:[di].TLFNSearchRec.Handle
  424.   int 21h
  425.   sbb bx,bx
  426.   and ax,bx
  427.   mov [DosError],ax
  428. end;
  429.  
  430. function LGetShortName(FileName: pchar; Result: pchar): word; assembler;
  431. { Return complete short name/path for input file/path in buffer }
  432. { Result (79 bytes) }
  433. asm
  434.   push ds
  435.   lds si,FileName
  436.   les di,Result
  437.   mov ax,7160h
  438.   mov cx,1
  439.   int 21h
  440.   pop ds
  441.   sbb bx,bx
  442.   and ax,bx
  443.   mov [DosError],ax
  444. end;
  445.  
  446. function LGetLongName(FileName: PChar; Result: PChar): word; assembler;
  447. { Return complete long name/path for input file/path in buffer }
  448. { Result (261 bytes) }
  449. asm
  450.   push ds
  451.   lds si,FileName
  452.   les di,Result
  453.   mov ax,7160h
  454.   mov cx,2
  455.   int 21h
  456.   pop ds
  457.   sbb bx,bx
  458.   and ax,bx
  459.   mov [DosError],ax
  460. end;
  461.  
  462. function LRenameFile(FromName,ToName: PChar): word; assembler;
  463. { Rename a file, supports long filenames. }
  464. asm
  465.   push ds
  466.   mov ax, $7156
  467.   lds dx, FromName
  468.   les di, ToName
  469.   int $21
  470.   jc @1
  471.   mov ax, 0
  472. @1:
  473.   pop ds
  474.   mov [DosError],ax
  475. end;           { LRenameFile }
  476.  
  477. function LCreateEmpty(fname: PChar): word; assembler;
  478. { Create an empty file with the given (long) name. }
  479. asm
  480.   push ds
  481.   mov ax, $716C 
  482.   mov bx, 000010b     { Open long file name for writing }
  483.   mov cx, 0
  484.   mov dx, 10001b      { Open if exists, create of not.  }
  485.   lds si, fname
  486.   mov di, 0
  487.   int $21
  488.   jc @1               { error creating file }
  489.   mov bx, ax          { ok, close it again  }
  490.   mov ah, $3E
  491.   int $21
  492.   jc @1               { error closing file }
  493.   mov ax, 0           { ok, return zero    }
  494. @1:
  495.   pop ds
  496.   mov [DosError],ax
  497. end;                { LCreateEmpty }
  498.  
  499. { Pascal-string based interface routines }
  500.  
  501. function LFNFindFirst(filespec: string; attr: word; var S: TLFNSearchRec): word;
  502. { Implement the FindFirst procedure. This routine will call the TP }
  503. { FindFirst if LFN is not supported, and will translate the result }
  504. { into the TLFNSearchRec variable.                                 }
  505. { NOTE: Under Win95, the filespec will be checked against both the }
  506. { long and the short filenames, so an additional check may be      }
  507. { necessary.                                                       } 
  508. begin
  509.   If LFNAble then
  510.   begin
  511.     filespec := filespec + #0;
  512.     LFindFirst(PChar(@Filespec[1]),Attr,S);
  513.     if (DosError=0) and (S.shortname[0]=#0) then
  514.     begin
  515.       move(S.name,S.shortname,sizeof(S.shortname)-1);
  516.       S.shortname[sizeof(S.shortname)-1]:=#0;
  517.     end;
  518.   end else
  519.   begin
  520.     FillChar(S,sizeof(S),0);
  521. {$IFDEF WINDOWS}
  522.     FileSpec:=FileSpec+#0;
  523.     FindFirst(PChar(@FileSpec[1]),Attr,PLFNShortSearchRec(@S)^.SRec);
  524. {$ELSE}
  525.     FindFirst(FileSpec,Attr,PLFNShortSearchRec(@S)^.SRec);
  526. {$ENDIF}
  527.     if DosError=0 then
  528.     begin
  529. {$IFDEF WINDOWS}
  530.       Move(PLFNShortSearchRec(@S)^.SRec.name,S.Name,13); S.name[13]:=#0;
  531. {$ELSE}
  532.      FillChar(S.Name,14,0);
  533.      Move(PLFNShortSearchRec(@S)^.SRec.name[1],S.Name,
  534.           byte(PLFNShortSearchRec(@S)^.SRec.name[0]));
  535. {$ENDIF}
  536.       DosTimeToLFN(PLFNShortSearchRec(@S)^.SRec.Time,S.LastMod);
  537.       S.Attr:=PLFNShortSearchRec(@S)^.SRec.Attr;
  538.       S.Size:=PLFNShortSearchRec(@S)^.SRec.Size;
  539.     end;
  540.   end;
  541.   LFNFindFirst:=DosError;
  542. end;     { LFNFindFirst }
  543.  
  544. function LFNFindNext(var S: TLFNSearchRec): word;
  545. { Implement the FindNext procedure. This routine will call the TP  }
  546. { FindNext if LFN is not supported, and will translate the result  }
  547. { into the TLFNSearchRec variable.                                 }
  548. { NOTE: Under Win95, the filespec will be checked against both the }
  549. { long and the short filenames, so an additional check may be      }
  550. { necessary.                                                       } 
  551. begin
  552.   If LFNAble then 
  553.   begin
  554.     LFindNext(S);
  555.     if (DosError=0) and (S.shortname[0]=#0) then
  556.     begin
  557.       move(S.name,S.shortname,sizeof(S.shortname)-1);
  558.       S.shortname[sizeof(S.shortname)-1]:=#0;
  559.     end; 
  560.   end else
  561.   begin
  562.     FindNext(PLFNShortSearchRec(@S)^.SRec);
  563.     if DosError=0 then
  564.     begin
  565. {$IFDEF WINDOWS}
  566.       Move(PLFNShortSearchRec(@S)^.SRec.name,S.Name,13); S.name[13]:=#0;
  567. {$ELSE}
  568.       FillChar(S.Name,14,0);
  569.       Move(PLFNShortSearchRec(@S)^.SRec.name[1],S.Name,
  570.            byte(PLFNShortSearchRec(@S)^.SRec.name[0]));
  571. {$ENDIF}
  572.       DosTimeToLFN(PLFNShortSearchRec(@S)^.SRec.Time,S.LastMod);
  573.       S.Attr:=PLFNShortSearchRec(@S)^.SRec.Attr;
  574.       S.Size:=PLFNShortSearchRec(@S)^.SRec.Size;
  575.     end;
  576.   end;
  577.   LFNFindNext:=DosError;
  578. end;   { LFNFindNext }                                             
  579.                                                  
  580. function LFNFindClose(var S: TLFNSearchRec): word;
  581. { Close the Win95 TLFNSearchRec structure. if LFN is not suppported, }
  582. { this routine does nothing.                                         }
  583. begin
  584.   If LFNAble then LFNFindClose:=LFindClose(S)
  585.   else LFNFindClose:=0;
  586. end;  {function}
  587.  
  588. function LFNShortName(LongName: string): ShortPathStr;
  589. { Returns the short name of the specified file. If LFN is not }
  590. { supported, returns the input filename.                      }
  591. var
  592.   P,Q: PChar;
  593.   i,len: integer;
  594. begin
  595.   if not LFNAble then
  596.   begin
  597.     LFNShortName:=LongName; Exit;
  598.   end;
  599.   len:=length(LongName);
  600.   for i:=1 to len do LongName[i-1]:=LongName[i]; LongName[len]:=#0;
  601.   P:=@Longname;
  602.   GetMem(Q,270); Q^:=#0;
  603.   if LGetShortName(P,Q)=0 then
  604.   begin
  605.     if Q^=#0 then LFNShortName:=LongName
  606.     else LFNShortName:=StrPas(Q);
  607.   end else LFNShortName:='';
  608.   FreeMem(Q,270);
  609. end;                     { ShortName }
  610.  
  611. function LFNLongName(ShortName: ShortPathStr): string;
  612. { Returns the long name of the specified file. If LFN is not }
  613. { supported, returns the input filename.                     }
  614. var
  615.   SRec: PLFNSearchRec;
  616.   P: PChar;
  617.   P0,D,N,E: PString;
  618.   i,len: integer;
  619. begin
  620.   LFNLongName:=ShortName; if not LFNAble then Exit;
  621.   len:=length(ShortName); if len=0 then Exit;
  622.   New(D); LFNFSplit(ShortName,D,Nil,Nil);
  623.   for i:=1 to len do ShortName[i-1]:=ShortName[i]; ShortName[len]:=#0;
  624.   GetMem(P0,270); P:=@PByteArray(P0)^[1]; P0^:=''; P^:=#0;
  625.   LGetLongName(PChar(@ShortName),P); PByteArray(P)^[256]:=#0;
  626.   P0^[0]:=Chr(StrLen(P));
  627.   Dispose(D);
  628.   if P^=#0 then LFNLongName:=ShortName
  629.   else LFNLongName:=StrPas(P);
  630.   FreeMem(P0,270);
  631. end;               { LFNLongName }
  632.  
  633. {====================================================================}
  634. { DERIVATIVE SERVICE ROUTINES.                                       }
  635. { This is a set of routines which mimic, as closely as possible, the }
  636. { equivalent routines in Turbo Pascal, except that they support      }
  637. { long filenames. In many cases, they are drop-in replacements, but  }
  638. { some are new.                                                      }
  639. {====================================================================}
  640.  
  641. procedure LFNUnpackTime(var LTime: comp; var DT: TDateTime);
  642. { Convert 64-bit time to date/time record }
  643. begin
  644.   UnpackTime(LFNTimeToDos(LTime),DT);
  645. end;
  646.  
  647. function LFNGetFAttr(var F; var Attr: word): integer;
  648. { Get the attributes of a file, using its File variable. }
  649. { The file should have been LFNAssign'ed first. Its not  }
  650. { strictly required, except for error checking.          }
  651. { Returns the DOS error code.                            }                      
  652. begin
  653.   LFNGetFAttr:=0; DosError:=0;
  654.   with PLFNFileParam(@F)^ do
  655.     if (Magic<>LFNMagic) or (not Initialized) then
  656.     begin
  657.       DosError:=2; LFNGetFAttr:=2; Exit;
  658.     end;
  659.   GetFAttr(F,Attr); LFNGetFAttr:=DosError;
  660. end;               { LFNGetFAttr }
  661.  
  662. function LFNFileExist(fname: string): boolean;
  663. { Returns TRUE if the file exists, and FALSE otherwise. }
  664. var
  665.   fl: file;
  666.   attr,i,len: word;
  667.   P: PChar;
  668. begin
  669.   if fName='' then
  670.   begin
  671.     LFNFileExist:=false; Exit;
  672.   end;
  673.   if LFNAble then
  674.   begin
  675.     len:=length(fname); for i:=1 to len do fname[i-1]:=fname[i];
  676.     fname[len]:=#0; LGetAttr(PChar(@fname),Attr)
  677.   end else
  678.   begin
  679.     Assign(fl,fname); GetFAttr(fl,Attr);
  680.   end;
  681.   LFNFileExist:=(DosError=0);
  682. end;                    { LFNFileExist }
  683.  
  684. function LFNFSearch(Path,DirList: string): string;
  685. { Search for a file in a semicolon-delimited list of directories. }
  686. { This is a drop-in replacement for FSearch (TP6), which I        }
  687. { personally find more useful than the later FileSearch.          }
  688. var
  689.   i,len,Ind: integer;
  690.   which: PChar;
  691.   tmp: PString;
  692.   found: boolean;
  693. begin
  694.   LFNFSearch:=''; if Path='' then Exit;
  695.   if LFNAble then
  696.   begin
  697.     if (DirList='') and not LFNFileExist(Path) then Exit;
  698.     if DirList='' then
  699.     begin
  700.       LFNFSearch:=Path; Exit;
  701.     end;
  702.     Ind:=1; New(tmp); found:=false;
  703.     while (DirList<>'') and (DirList[1]=';') do delete(DirList,1,1);
  704.     repeat
  705.       tmp^:='';
  706.       while (Ind<=length(DirList)) and (DirList[Ind]<>';') do
  707.       begin
  708.         tmp^:=tmp^+DirList[Ind]; inc(Ind);
  709.       end;
  710.       while (Ind<=length(DirList)) and (DirList[Ind]=';') do inc(Ind);
  711.       if Ind>length(DirList) then Ind:=0 else inc(Ind);
  712.       if tmp^<>'' then
  713.       begin
  714.         if tmp^[length(tmp^)]<>'\' then tmp^:=tmp^+'\';
  715.         if LFNFileExist(tmp^+Path) then
  716.         begin
  717.           LFNFSearch:=LFNFExpand(tmp^+Path); found:=true;
  718.         end;
  719.       end;
  720.     until found or (Ind=0);
  721.     Dispose(tmp);
  722.   end else
  723.   begin
  724. {$IFDEF WINDOWS}
  725.     GetMem(Which,256);
  726.     len:=length(Path); for i:=1 to len do Path[i-1]:=Path[i]; Path[len]:=#0;
  727.     len:=length(DirList); for i:=1 to len do DirList[i-1]:=DirList[i]; DirList[len]:=#0;
  728.     FileSearch(which,PChar(@Path),PChar(@DirList));
  729.     LFNFSearch:=StrPas(Which); FreeMem(Which,256);
  730. {$ELSE}
  731.     LFNFSearch:=FSearch(Path,DirList);
  732. {$ENDIF}
  733.   end;
  734. end;                     { LFNFSearch }
  735.  
  736. procedure LFNFSplit(Path: string; Dir,Name,Ext: PString);
  737. { An almost drop-in replacement for the TP6 FSplit, which supports LFN.   }
  738. { The additional difference is that the arguments are passed as pointers, }
  739. { rather than VAR variables. This is so that if a file segment is not     }
  740. { needed, one can pass NIL in the respective variable, and it will not    }
  741. { be returned.                                                            }
  742. var
  743.   StrPt,StrSlash,StrEnd: integer;
  744. begin
  745.   StrEnd:=length(Path);
  746.   StrPt:=StrEnd; StrSlash:=0;
  747.   while(StrPt>0) and (Path[StrPt]<>'.') and (Path[StrPt]<>'\') do dec(StrPt);
  748.   if (StrPt>0) and (Path[StrPt]='.') then  { found extension }
  749.   begin
  750.     StrSlash:=StrPt-1;
  751.     while (StrSlash>0) and (Path[StrSlash]<>'\') do dec(StrSlash);
  752.   end else if (StrPt>0) and (Path[StrPt]='\') then  { No extension }
  753.   begin
  754.     StrSlash:=StrPt; StrPt:=StrEnd+1;
  755.   end else if StrPt=0 then   { All name }
  756.   begin
  757.     StrPt:=StrEnd+1; StrSlash:=0;
  758.   end;
  759.  
  760.   if Dir<>Nil then
  761.   begin
  762.     Dir^:='';
  763.     if StrSlash>0 then Dir^:=Copy(Path,1,StrSlash);
  764.   end;
  765.   if Name<>Nil then
  766.   begin
  767.     Name^:='';
  768.     if StrPt>StrSlash+1 then Name^:=Copy(Path,StrSlash+1,StrPt-StrSlash-1);
  769.   end;
  770.   if Ext<>Nil then
  771.   begin
  772.     Ext^:='';
  773.     if StrPt<=StrEnd then Ext^:=Copy(Path,StrPt,255);
  774.   end;
  775. end;                   { LFNFSplit }
  776.  
  777. function LFNFExpand(Path: string): string;
  778. { Drop-in replacement for the TP6 FExpand, which supports LFN. }
  779. { Personally, I prefer it to the later FileExpand.             }
  780. var
  781.   D,N,E,P: PString;
  782.   i,j,ndots: integer;
  783. begin
  784.   for i:=1 to length(Path) do if Path[i]='/' then Path[i]:='\';
  785.   LFNFExpand:='';
  786.   GetMem(P,270);
  787. {$IFDEF WINDOWS}
  788.   FileExpand(PChar(P)+1,'.'); P^[0]:=chr(StrLen(PChar(P)+1));
  789. {$ELSE}
  790.   P^:=FExpand('.'); 
  791. {$ENDIF}
  792.   if (P^<>'') and (P^[length(P^)]<>'\') then P^:=P^+'\';
  793.   P^:=LFNLongName(P^);
  794.   ndots:=0;
  795.   while (ndots<length(Path)) and (Path[Ndots+1]='.') do inc(ndots);
  796.   if (length(Path)>1) and (UpCase(Path[1]) in ['A'..'Z']) and (Path[2]=':') then
  797.     P^:=Path         { Fully qualified }
  798.   else if Path[1]='\' then        { Only drive missing }
  799.     P^:=Copy(P^,1,2)+Path
  800.   else begin
  801.     for i:=1 to ndots-1 do    { relative filenames, multiple dots }
  802.     begin
  803.       if length(P^)>3 then
  804.       begin
  805.         j:=length(P^)-1;
  806.         while (j>3) and (P^[j]<>'\') do dec(j);
  807.         P^[0]:=Chr(j);
  808.       end;
  809.       delete(Path,1,1);
  810.     end;
  811.     if Pos('.\',Path)=1 then Delete(Path,1,2)
  812.     else if Pos('.',Path)=1 then Delete(Path,1,1);
  813.     P^:=P^+Path;
  814.   end;
  815.   LFNFExpand:=P^;
  816.   FreeMem(P,270);
  817. end;                     { LFNFExpand }
  818.  
  819. procedure CanonicalFname(var S: string);
  820. { This routine takes a filename and changes its case to a canonical form: }
  821. { 1. Without LFN support, lowercase.                                      }
  822. { 1. For existing short filenames, or dir names, lowercase.               }
  823. { 2. For existing long filenames, the system-supplied case.               }
  824. { 3. For non-existing filenames, expand the existing part of the path,    }
  825. {    and leave the rest unchanged.                                        }
  826. { In all cases '/' is changed to '\'.                                     }
  827. type
  828.   TBf = array[1..3] of string;
  829. var
  830.   lname,sname,res: Pstring;
  831.   Buf: ^TBf;
  832.   i,j: integer;
  833.   exists: boolean;
  834.  
  835. procedure StrLwr(var L: string);
  836. var
  837.   i: integer;
  838. begin
  839.   for i:=1 to length(L) do if L[i] in ['A'..'Z'] then
  840.     L[i]:=Chr(Ord(L[i])-Ord('A')+Ord('a'));
  841. end;
  842.  
  843. begin
  844.   for i:=1 to length(S) do if S[i]='/' then S[i]:='\';
  845.   if LFNAble then
  846.   begin
  847.     New(Buf);
  848.     Buf^[1]:='';
  849.     repeat
  850.       i:=Pos('\',S); if i=0 then i:=length(S);
  851.       if S[i]='\' then exists:=LFNFileExist(Buf^[1]+Copy(S,1,i)+'.')
  852.       else exists:=LFNFileExist(Buf^[1]+Copy(S,1,i));
  853.       if exists then
  854.       begin
  855.         Buf^[2]:=LFNShortName(Buf^[1]+Copy(S,1,i));
  856.         Buf^[3]:=LFNLongName(Buf^[2]);
  857.         j:=length(Buf^[2])-1; while (j>0) and (Buf^[2][j]<>'\') do dec(j);
  858.         Delete(Buf^[2],1,j);
  859.         j:=length(Buf^[3])-1; while (j>0) and (Buf^[3][j]<>'\') do dec(j);
  860.         Delete(Buf^[3],1,j);
  861.         if Buf^[3]=Buf^[2] then StrLwr(Buf^[3]);
  862.         Buf^[1]:=Buf^[1]+Buf^[3];
  863.         delete(S,1,i);
  864.       end;
  865.     until (not exists) or (S='');
  866.     S:=Buf^[1]+S;
  867.     Dispose(Buf);
  868.   end else StrLwr(S);
  869. end;                { CanonicalFname }
  870.  
  871. function CanonicalFilename(fname: PChar): PChar;
  872. begin
  873.   CanonicalFName(PChar2PString(fname)^);
  874.   fname:=PString2PChar(PString(fname));
  875.   CanonicalFilename:=fname;
  876. end;
  877.  
  878. {=========================================================================}
  879. { BINARY AND TEXT FILE INPUT/OUTPUT ROUTINES.                             }
  880. { This set of routines is an interface between the LFN API and the Pascal }
  881. { style input/output routines. It uses ordinary text and file variables,  }
  882. { storing special info in the UserData field. The variable is then fully  }
  883. { compatible with the Pascal read(ln), write(ln), BlockRead, BlockWrite,  }
  884. { etc input/output routines.                                              }
  885. { All the functions return the DOS error code, and also put it into       }
  886. { DOSERROR. The global "LFNRuntimeError" determines if runtime errors     }
  887. { will be generated (by default, no.)                                     }                    
  888. {=========================================================================}
  889.  
  890. procedure LFNNew(var F; IsText: boolean);
  891. { This routine prepares a text or file variable for LFN use. It allocates }
  892. { memory for the long name, and initializes the entries in the UserData.  }
  893. { It must be called before any other.                                     }
  894. { The "IsText" flag tells if the variable is of type "file" or "text".    }
  895. begin
  896.   with PLFNFileParam(@F)^ do
  897.   begin
  898.     TextFile:=IsText;
  899.     Initialized:=false;
  900.     Magic:=LFNMagic;
  901.     lfname:=Nil; plfname:=Nil;
  902.     if LFNAble then
  903.     begin
  904.       GetMem(lfname,270); FillChar(lfname^,270,0);
  905.       plfname:=PChar(@PByteArray(lfname)^[1]);
  906.     end;
  907.   end;
  908. end;                    { LFNNew }
  909.  
  910. function LFNAssign(var F; name: string): integer;
  911. { This routine replaces the Pascal "Assign" routine. For existing files, }
  912. { it first determines the short name, and then invokes "Assign". If the  }
  913. { file does not exist, it only stores the information in the UserData    }
  914. { fields, since the equivalent short name is not known. The assign       }
  915. { operation is then deferred to the first "LFNRewrite" call.             }
  916. { LFNAssign may be called for the same variable for different filenames, }
  917. { so long as the type (file or text) is the same.                        }
  918. var
  919.   tmp,fname: PString;
  920.   IsText: boolean;
  921.   P: PChar;
  922. begin
  923.   if PLFNFileParam(@F)^.Magic<>LFNMagic then
  924.   begin
  925.     DosError:=LFNErr_NotAllocated;
  926.     LFNAssign:=DosError;
  927. {$IFDEF WINDOWS}
  928.     MessageBox(0,'Bug, LFNAssign',Nil,mb_ok);    { for debugging }
  929. {$ENDIF}
  930.     Exit;
  931.   end;   
  932.   LFNAssign:=0; DosError:=0;
  933.   if LFNAble then
  934.   begin
  935.     GetMem(fname,270);
  936.     if LFNFileExist(name) then
  937.     begin
  938.       fname^:=LFNShortName(name);
  939.       PByteArray(fname)^[length(fname^)+1]:=#0;
  940.     end else fname^:='';
  941.   end else fname:=@name;
  942.   with PLFNFileParam(@F)^ do
  943.   begin
  944.     if fname^='' then Initialized:=false
  945.     else begin
  946.       IsText:=TextFile; tmp:=lfname; P:=plfname;
  947.       if IsText then Assign(text(F),fname^) else assign(file(F),fname^);
  948.       Initialized:=true;
  949.       TextFile:=IsText; lfname:=tmp; plfname:=P;
  950.       Magic:=LFNMagic;
  951.     end;
  952.     if LFNAble then
  953.     begin
  954.       lfname^:=name;
  955.       PByteArray(lfname)^[length(lfname^)+1]:=#0;
  956.     end;
  957.   end;
  958.   if LFNAble then FreeMem(fname,270);
  959. end;                       { LFNAssign }
  960.  
  961. function LFNRewrite(var F; RecLen: word): integer;
  962. { This routine readies a file for output. If the file does not yet exist, }
  963. { it creates an empty file to get the system-determined short name, and   }
  964. { performs a deferred Assign, since at Assign time a short name was not   }
  965. { yet available (see description of LFNAssign).                           }
  966. { The routine returns 0 if successful, and the DOS errorcode if not.      } 
  967. var
  968.   tmp,fname: PString;
  969.   IsText: boolean;
  970.   P: PChar;
  971.  
  972. function Err(e: byte): byte;
  973. begin
  974.   LFNRewrite:=e; DosError:=e; Err:=e;
  975.   if LFNRuntimeErrors and (e<>0) then RunError(e);
  976. end;
  977.  
  978. begin
  979.   Err(0);
  980.   if PLFNFileParam(@F)^.Magic<>LFNMagic then
  981.   begin
  982. {$IFDEF WINDOWS}
  983.     MessageBox(0,'Bug, LFNRewrite',Nil,mb_ok);   { for debugging }
  984. {$ENDIF}
  985.     Err(LFNErr_NotAllocated); Exit;
  986.   end;   
  987.   if LFNAble then
  988.   with PLFNFileParam(@F)^ do
  989.   begin
  990.     if not Initialized then    { create the file, so we can get a valid short name }
  991.     begin
  992.       if Err(LCreateEmpty(plfname))=0 then
  993.       begin
  994.         New(fname);
  995.         fname^:=LFNShortName(lfname^);
  996.         IsText:=TextFile; tmp:=lfname; P:=plfname;
  997.         if IsText then Assign(text(F),fname^) else assign(file(F),fname^);
  998.         Initialized:=true;
  999.         TextFile:=IsText; lfname:=tmp; plfname:=P;
  1000.         Magic:=LFNMagic;
  1001.       end;
  1002.     end;
  1003.     if Initialized then
  1004.     begin
  1005.       {$I-}
  1006.       if TextFile then Rewrite(text(F))
  1007.       else if RecLen=0 then Rewrite(file(F))
  1008.       else Rewrite(file(F),RecLen);
  1009.       Err(IoResult);
  1010.       {$I+}
  1011.     end;
  1012.   end else with PLFNFileParam(@F)^ do
  1013.   if Initialized then
  1014.   begin
  1015.     {$I-}
  1016.     if TextFile then Rewrite(text(F))
  1017.     else if RecLen=0 then rewrite(file(F))
  1018.     else Rewrite(file(F),RecLen);
  1019.     Err(IoResult);
  1020.     {$I+}
  1021.   end;
  1022. end;               { LFNRewrite }
  1023.  
  1024. function LFNAppend(var F; RecLen: word): integer;
  1025. { This routines opens a previously LFNAssigned for output at the EOF. }
  1026. { Its not really necessary, except that it performs additional error  }
  1027. { checking to make  sure that the file was properly initialized.      }
  1028. { Also, in contrast to the TP Append, if the file does not exist the  }
  1029. { routine calls LFNRewrite to create and open it.                     }
  1030. { The routine returns 0 if successful, and the DOS errorcode if not.  }
  1031.  
  1032. function Err(e: byte): byte;
  1033. begin
  1034.   LFNAppend:=e; DosError:=e; Err:=e;
  1035.   if LFNRuntimeErrors and (e<>0) then RunError(e);
  1036. end;
  1037.  
  1038. begin
  1039.   Err(0);
  1040.   if PLFNFileParam(@F)^.Magic<>LFNMagic then
  1041.   begin
  1042.     Err(LFNErr_NotAllocated); Exit;
  1043.   end;
  1044.   with PLFNFileParam(@F)^ do
  1045.   begin
  1046.     if Magic<>LFNMagic then
  1047.     begin
  1048.       Err(LFNErr_NotAllocated); Exit;
  1049.     end else if not TextFile then
  1050.     begin
  1051.       Err(LFNErr_NotATextFile); Exit;
  1052.     end else if not Initialized then Err(LFNRewrite(F,RecLen))
  1053.     else begin
  1054.       {$I-}
  1055.       Append(text(F)); Err(IoResult);
  1056.       {$I+}
  1057.     end;
  1058.   end;
  1059. end;             { LFNAppend }
  1060.  
  1061. function LFNReset(var F; RecLen: word): integer;
  1062. { This routines opens a file for input, instead of "reset". Its not really }
  1063. { necessary, except that it performs additional error checking to make     }
  1064. { sure that the file was properly initialized.                             }
  1065. { The routine returns 0 if successful, and the DOS errorcode if not.       }
  1066.  
  1067. procedure Err(e: byte);
  1068. begin
  1069.   LFNReset:=e; DosError:=e;
  1070.   if LFNRuntimeErrors and (e<>0) then RunError(e);
  1071. end;
  1072.  
  1073. begin
  1074.   Err(0);
  1075.   if PLFNFileParam(@F)^.Magic<>LFNMagic then
  1076.   begin
  1077. {$IFDEF WINDOWS}
  1078.     MessageBox(0,'Bug, LFNReset',Nil,mb_ok);   { for debugging }
  1079. {$ENDIF}
  1080.     Err(LFNErr_NotAllocated); Exit;
  1081.   end;
  1082.   with PLFNFileParam(@F)^ do
  1083.   begin
  1084.     if not Initialized then LFNReset:=LFNErr_UnInitialized
  1085.     else begin
  1086.       {$I-}
  1087.       if TextFile then Reset(text(F))
  1088.       else if RecLen=0 then Reset(file(F))
  1089.       else Reset(file(F),RecLen);
  1090.       Err(IoResult);
  1091.       {$I+}
  1092.     end;
  1093.   end;
  1094. end;             { LFNReset }
  1095.  
  1096. function LFNErase(var F): integer;
  1097. { This routines erases a previously LFNAssigned, but not opened, file. }
  1098. { Its not really necessary, except that it performs additional error   }
  1099. { checking to make  sure that the file was properly initialized. Also, }
  1100. { it re-assignes the file so it will be properly ready for a rewrite.  }
  1101. { The routine returns 0 if successful, and the DOS errorcode if not.   }
  1102. var
  1103.   S: PString;
  1104.   S1: PChar;
  1105.  
  1106. function Err(e: byte): byte;
  1107. begin
  1108.   LFNErase:=e; DosError:=e; Err:=e;
  1109.   if LFNRuntimeErrors and (e<>0) then RunError(e);
  1110. end;
  1111.  
  1112. begin
  1113.   with PLFNFileParam(@F)^ do
  1114.   begin
  1115.     LFNErase:=0;
  1116.     if (Magic<>LFNMagic) then
  1117.     begin
  1118.       Err(LFNErr_NotAllocated); Exit;
  1119.     end else if (not Initialized) then
  1120.     begin
  1121.       Err(LFNErr_UnInitialized); Exit;
  1122.     end;
  1123.     LFNClose(F);
  1124.     if not LFNAble then
  1125.     begin
  1126.       GetMem(S,81); S1:=PChar(@PByteArray(S)^[1]);
  1127.       Move(SName,S1^,80); S^:=Chr(StrLen(S1));
  1128.     end;
  1129.     {$I-}
  1130.     if TextFile then Erase(text(F)) else Erase(file(F));
  1131.     if Err(IoResult)=0 then
  1132.     begin
  1133.       if LFNAble then LFNAssign(F,lfname^)
  1134.       else begin
  1135.         LFNAssign(F,S^); FreeMem(S,81);
  1136.       end;
  1137.     end;
  1138.     {$I+}
  1139.   end;
  1140. end;                   { LFNErase }
  1141.  
  1142. function LFNClose(var F): integer;
  1143. { This routines closes a previously LFNAssigned and opened file.     }
  1144. { Its not really necessary, except that it performs additional error }
  1145. { checking to make  sure that the file was properly initialized.     }
  1146. { The routine returns 0 if successful, and the DOS errorcode if not. }
  1147.  
  1148. function Err(e: byte): byte;
  1149. begin
  1150.   LFNClose:=e; DosError:=e; Err:=e;
  1151.   if LFNRuntimeErrors and (e<>0) then RunError(e);
  1152. end;
  1153.  
  1154. begin
  1155.   Err(0);
  1156.   with PLFNFileParam(@F)^ do
  1157.   begin
  1158.     if Magic<>LFNMagic then
  1159.     begin
  1160.       Err(LFNErr_NotAllocated); Exit;
  1161.     end else if not Initialized then
  1162.     begin
  1163.       Err(LFNErr_UnInitialized); Exit;
  1164.     end;
  1165.     {$I-}
  1166.     if TextFile then close(text(F)) else close(file(F));
  1167.     Err(IoResult);
  1168.     {$I+}
  1169.   end;
  1170. end;                   { LFNClose }
  1171.  
  1172. procedure LFNDispose(var F);
  1173. { This routine disposes of the additional memory allocated by LFNNew, }
  1174. { and cleans up the UserData fields. If the file is open, it also     }
  1175. { closes it, so that there is no need to call LFNClose previously.    }
  1176. begin
  1177.   with PLFNFileParam(@F)^ do
  1178.   begin
  1179.     if (Magic<>LFNMagic) or (not Initialized) then Exit;
  1180.     LFNClose(F);
  1181.     if lfname<>Nil then FreeMem(lfname,270);
  1182.     lfname:=Nil; plfname:=Nil; Initialized:=false; Magic:='';
  1183.   end;
  1184. end;                 { LFNDispose }
  1185.  
  1186. function LFNRename(var F; NewName: string): integer;
  1187. { This routines renames a previously LFNAssigned, but not opened, file. }
  1188. { The file variable is then re-assigned to the new name.                }
  1189. { The routine returns 0 if successful, and the DOS errorcode if not.    }
  1190. var
  1191.   i,len: integer;
  1192.  
  1193. function Err(e: byte): byte;
  1194. begin
  1195.   LFNRename:=e; DosError:=e; Err:=e;
  1196.   if LFNRuntimeErrors and (e<>0) then RunError(e);
  1197. end;
  1198.  
  1199. begin
  1200.   Err(0);
  1201.   if NewName='' then Exit;
  1202.   with PLFNFileParam(@F)^ do
  1203.   begin
  1204.     if Magic<>LFNMagic then
  1205.     begin
  1206.       Err(LFNErr_NotAllocated); Exit;
  1207.     end else if not Initialized then
  1208.     begin
  1209.       Err(LFNErr_UnInitialized); Exit;
  1210.     end;
  1211.     if not LFNAble then   { The usual TP stuff }
  1212.     begin
  1213.       {$I-}
  1214.       if TextFile then Rename(text(F),NewName) else Rename(file(F),NewName);
  1215.       Err(IoResult);
  1216.       {$I+}
  1217.     end else                       { LFN }
  1218.     begin
  1219.       len:=length(NewName);
  1220.       for i:=1 to len do NewName[i-1]:=NewName[i]; NewName[len]:=#0;
  1221.       if Err(LRenameFile(plfname,PChar(@NewName)))=0 then
  1222.       begin
  1223.         for i:=len downto 1 do
  1224.           NewName[i]:=NewName[i-1]; NewName[0]:=chr(len);
  1225.         LFNAssign(F,NewName);
  1226.       end;
  1227.     end;
  1228.   end;
  1229. end;                    { LFNRename }
  1230.  
  1231. begin
  1232.   LFNAble:=SupportsLFN;
  1233. end.
  1234.